/*
 ============================================================================
 Name        : gameserver.c
 Author      : xuguiping
 Version     :
 Copyright   : copyright xuguiping
 Description : Hello World in C, Ansi-style
 ============================================================================
 */



#include <stdio.h>
#include <stdlib.h>

#include <signal.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <ctype.h>
#include <bits/types.h>
#include <time.h>
#include <pthread.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>

#include "hashtable.h"
#include "list.h"
#include "gameserver.h"
#include "workthread.h"

list_t * connectionlist;
hashtable_t * connectiontable;
pthread_mutex_t connectionlistlock;

extern list_t * requestlist;
extern pthread_mutex_t requestlistlock;


#define SERVERPORT 2000

struct event_base* base;
struct event * timer_ev;
struct timeval tv;

int verbose=0;

int totalconncount=0;
int totalrequestcount=0;

char *allowdomain="*";
int serverport=SERVERPORT;

void connection_free(connection_t * pconn){
	pthread_mutex_lock(&connectionlistlock);
	char fds[10]={0};
	sprintf(fds,"%d",pconn->fd);
	node_t * node=hash_value(fds,connectiontable);
	if(node!=NULL){
		list_delete(node,connectionlist);
		hash_remove(fds,connectiontable);
		if(pconn->user!=NULL){
			userexit(pconn->user);
		}
	}
	pthread_mutex_unlock(&connectionlistlock);
	bufferevent_free(pconn->buf_evt);
	free(pconn);
}

void on_bufferread(struct bufferevent * buf_evt,void * argv){
	char t=0;
	int i;
	connection_t * pconn=(connection_t *)argv;
	int n=0;
	int c = bufferevent_read(buf_evt,&t,1);

	while(c>0){
		char str[1024]={0};
		i=0;
		while(c>0){
			if(t=='\0'){
				break;
			}else if(t=='\r'){
				bufferevent_read(buf_evt,&t,1);
				break;
			}else if(t=='\n'){
				break;
			}
			str[i]=t;
			i++;
			c=bufferevent_read(buf_evt,&t,1);
		}

		if(i>0){
			char policy_file[1024]={0};
			const char * pf = "<?xml version=\"1.0\"?>\n<cross-domain-policy>\n<site-control permitted-cross-domain-policies=\"all\"/>\n<allow-access-from domain=\"%s\" secure=\"false\" to-ports=\"%d\"/>\n</cross-domain-policy>";
				sprintf(policy_file,pf,allowdomain,serverport);
			if(str[0]=='<'){
				writebuffer(policy_file,strlen(policy_file)+1,pconn);
			}else{
				request_t * request=malloc(sizeof(request));
				request->querystring=malloc(i+1);

				request->pconn=pconn;
				strcpy(request->querystring,str);

				pthread_mutex_lock(&requestlistlock);
				list_push_tail(request,requestlist);
				pthread_mutex_unlock(&requestlistlock);

				printf("i:%d,str:%s\n",i,str);
			}
		}

		n+=1;
		c = bufferevent_read(buf_evt,&t,1);
	}
}

int writebuffer(void * buffer,int len,connection_t * pconn){
	//struct bufferevent  * bet=pconn->buf_evt;
	//struct evbuffer* pOutstream = bufferevent_get_output(bet);
	//int ret=evbuffer_add(pOutstream,buffer,len);
	//int ret=send(pconn->fd,buffer,len,0);
	int ret=bufferevent_write(pconn->buf_evt,buffer,len);
	return ret;
}

int writecmd(connection_t * pconn,char *cmd,char *format,...){
	char buffer1[1000]={0};
	char buffer[1024]={0};
	if(format!=NULL){
		va_list  args;
		va_start(args,format);
		vsprintf(buffer1,format,args);
		va_end(args);
		sprintf(buffer,"%s %s\r\n",cmd,buffer1);
	}else{
		sprintf(buffer,"%s\r\n",cmd);
	}
	return writebuffer((void *)buffer,strlen(buffer),pconn);
}

void on_buffererror(struct bufferevent * buf_evt,short what,void * argv){
	connection_t * pconn=(connection_t *)argv;

	if(what & BEV_EVENT_EOF){
		printf("close connection fd: %d\n",pconn->fd);
		bufferevent_disable(buf_evt,EV_READ | EV_WRITE);
		connection_free(pconn);
	}
}

connection_t * connection_new(int fd,struct event_base* eventbase){
	connection_t * pconn=malloc(sizeof(connection_t));
	memset(pconn,0,sizeof(connection_t));
	pconn->fd=fd;

	evutil_make_socket_nonblocking(fd);
	pconn->buf_evt=bufferevent_socket_new(base,fd,BEV_OPT_DEFER_CALLBACKS);
	bufferevent_setcb(pconn->buf_evt,on_bufferread,NULL,on_buffererror,pconn);

	bufferevent_base_set(eventbase,pconn->buf_evt);
	//bufferevent_setwatermark(pconn->buf_evt, EV_READ, 0, 1024);
	bufferevent_enable(pconn->buf_evt, EV_READ);

	time(&pconn->activetime);

	char fds[10]={0};
	sprintf(fds,"%d",fd);
	pthread_mutex_lock(&connectionlistlock);
	node_t * node=list_push_tail(pconn,connectionlist);
	hash_insert(fds,node,connectiontable);
	pthread_mutex_unlock(&connectionlistlock);

	return pconn;
}


extern int daemonize(int nochdir, int noclose);
extern int sigignore(int sig);

void on_accept(int fd, short event, void* arg)
{
    struct sockaddr_in cli_addr;
    int newfd;
    socklen_t sin_size;
    sin_size = sizeof(struct sockaddr_in);
    newfd = accept(fd, (struct sockaddr*)&cli_addr, &sin_size);

    if(newfd<0){
    	perror("connfd<0");
    	return;
    }

    totalconncount++;
    evutil_make_socket_nonblocking(newfd);
    connection_new(newfd,base);
	printf("accept connection\n");

}


void usage(){
	printf("-t <threadcount>    Set work thread count,default 20\n"
		   "-p <port>           Set Listen Port,default 5000\n"
		   "-d                  Start with Deamon\n"
		   "-h                  Show This Help\n"
		   "\n"
		   "Write by XUGP 2011,qhsoft@gmail.com\n"
	);
}

static void sig_handler(const int sig) {
    //printf("SIGINT handled.\n");
    exit(EXIT_SUCCESS);
}


void on_timer(int fd, short event, void *argc){

}

int main(int argc, char * argv[]) {

	int c;
	int threadcount=1;
	serverport=SERVERPORT;

    int do_daemonize = 0;


	/* handle SIGINT */
    signal(SIGINT, sig_handler);

	while((c=getopt(argc,argv,"t:hp:d"))!=-1){
		switch(c){
		case 't':
			threadcount=atoi(optarg);
			break;
		case 'h':
			usage();
            exit(EXIT_SUCCESS);
		case 'v':
			verbose=1;
			break;
		case 'p':
			serverport=atoi(optarg);
			break;
		case 'd':
			do_daemonize = 1;
			break;
		default:
			fprintf(stderr, "Illegal argument \"%c\"\n", c);
			return 1;
		}
	}

	/* daemonize if requested */
    /* if we want to ensure our ability to dump core, don't chdir to / */
    if (do_daemonize==1) {
        if (sigignore(SIGHUP) == -1) {
            perror("Failed to ignore SIGHUP");
        }
        if (daemonize(0, 0) == -1) {
            fprintf(stderr, "failed to daemon() in order to daemonize\n");
            exit(EXIT_FAILURE);
        }
    }


    connectionlist=list_create();
    connectiontable=create_hashtable(500000);
    pthread_mutex_init(&connectionlistlock,PTHREAD_MUTEX_TIMED_NP);

    initthreads(threadcount);

    struct sockaddr_in my_addr;
    int sock;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    int yes = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
    memset(&my_addr, 0, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(serverport);
    my_addr.sin_addr.s_addr = INADDR_ANY;
    int ret=bind(sock, (struct sockaddr*)&my_addr, sizeof(struct sockaddr));
    if(ret==-1){
    	printf("Port be Used\n");
		exit(EXIT_FAILURE);
    }

    listen(sock, 10);
	printf("Server Started,ThreadCount:%d,port:%d!!!\n",threadcount,serverport);

	struct event *listen_ev;

    base = event_base_new();
    listen_ev=event_new(base,sock,EV_READ|EV_PERSIST,on_accept,NULL);

    event_add(listen_ev, NULL);

    timer_ev=event_new(base,0,EV_TIMEOUT,on_timer,NULL);
    tv.tv_sec=30;
    tv.tv_usec=0;
    event_add(timer_ev,&tv);
    event_base_dispatch(base);


	return EXIT_SUCCESS;
}
